/**
 * @author Shen Yi - bm2736892@gmail.com
 */

var MaterialManager = {
	_materials : [],
	_options : [],
	
	_pack : null,
	_client : null,
	
	_renderTargetWidth : 1024,
	_renderTargetHeight : 1024,
	
	init : function(pack, client){
		this._pack = pack;
		this._client = client;
	},
	
	/**
	 * 
	 * @param {String} name
	 */
	getMaterial : function(name){
		if(isset(name)){
			return this._materials[name];
		}
		return this._materials;
	},
	
	/**
	 * 
	 * @param {String} name
	 */
	getOption : function(name){
		if(isset(name)){
			return this._options[name];
		}
		return this._options;
	},
	/**
	 * 
	 * @param {String} name
	 * @param {Object} material
	 */
	addMaterial : function(name, material){
		this._materials[name] = material;
	},
	createFromImportedMaterial : function(importMaterial, templateMaterialName, callback){
		if(templateMaterialName){
			var material = this.copyMaterial(importMaterial.name, templateMaterialName);
			this._updateMaterialFromImport(material, importMaterial);
			return material;
		}else{
			this.loadFxFile(importMaterial.name, "media/material/phong.json", function(finalMaterial){
				MaterialManager._updateMaterialFromImport(finalMaterial, importMaterial);
				callback(finalMaterial);
			})
		}
	},
	_updateMaterialFromImport : function(targetMat, importMat){
		if(importMat.getParam("ambient")){
			MaterialManager.updateParam(targetMat.name, "ambient", importMat.getParam("ambient").value);
		}
		if(importMat.getParam("specular")){
			MaterialManager.updateParam(targetMat.name, "specular", importMat.getParam("specular").value);
		}
		if(importMat.getParam("shininess")){
			MaterialManager.updateParam(targetMat.name, "shininess", importMat.getParam("shininess").value);
		}
		if(importMat.getParam("diffuse")){
			MaterialManager.updateParam(importMat.name, "diffuse", importMat.getParam("diffuse").value);
			MaterialManager.updateParam(importMat.name, "hasTex", 0);
		}else if(importMat.getParam("diffuseSampler")){
			var sampler = importMat.getParam("diffuseSampler").value;
			TextureManager.addSampler(importMat.name, sampler);
			MaterialManager.updateTexture(importMat.name, "diffuseSampler", sampler);
		}
	},
	/**
	 * 
	 * @param {string} name
	 */
	createMaterial : function(name){
		var effect = this._pack.createObject("Effect");
		
		var material = this._pack.createObject("Material");
		material.effect = effect;
		material.name = name;

		this._materials[name] = material;
		//bind material to main camera defaultly
		this.bindCamera(name, "main_camera");

		return material;
	},
	
	/**
	 * 
	 * @param {String} name
	 * @param {String} shaderPath
	 */
	createMaterialFromShader : function(name, shaderPath){
		var material = this.createMaterial(name);
		//shader files is loaded synchronously
		o3djs.effect.loadEffect(material.effect, shaderPath);
		material.effect.createUniformParameters(material);
		
		return material;
	},
	
	/**
	 * bind a camera to the material, so the renderNode of the camera begin to the tree of the transform,
	 * it will decide whether to render this material(the drawlist option is a member of material)
	 * @param {String} materialName
	 * @param {String} cameraName
	 * @param {Boolean|String} option zOrdered|passName
	 */
	bindCamera : function(materialName, cameraName, option){
		option = isset(option) ? option : false;

		var material = this.getMaterial(materialName);
		var camera = SceneManager.getCamera(cameraName);
		if(typeof(option) == "string"){
			material.drawList = camera.getPass(option).drawList
		}
		else if(option){			//zOrdered
			material.drawList = camera.getViewInfo().zOrderedDrawList;
		}else{
			material.drawList = camera.getViewInfo().performanceDrawList;
		}
	},
	
	/**
	 * create a material from the file, which has a fx like format
	 * @param {string} filePath
	 * @param {function} callback callback(material)
	 */
	loadFxFile : function(name, filePath, callback){
		if(isset(this._options[name])){
			return;
		}
		else{
			$.post(filePath, {}, function(json){
				jsonString = json;
				try{
					jsonObj = eval("("+json+")");
					MaterialManager.createFxMaterial(name, jsonObj, callback);
				}
				catch(e){
					alert("error : " + e);
				}
			});
		}
	},
	
	/**
	 * 
	 * @param {String} fxName
	 * @param {Object} jsonObj
	 * @param {function} callback callback(material)
	 */
	createFxMaterial : function(fxName, jsonObj, callback){
		var shaderString = "";
		
		if(isset(jsonObj.Variable)){
			for(var variableName in jsonObj.Variable){
				var variable = jsonObj.Variable[variableName];
				shaderString += variable.semantic + " " + variableName + "; ";
			}
		}
		
		if(isset(jsonObj.Texture)){
			for(var textureName in jsonObj.Texture){
				var texture = jsonObj.Texture[textureName];
				if(!(isset(texture.src) && texture.src) && texture.type == "2D"){
					var sampler = TextureManager.createSampler(texture.src, texture);
					//add texture to sampler later while the texture is a shadow map
					if(texture.semantic != "SHADOWMAP"){
						sampler.texture = TextureManager.createRenderTarget2D(textureName, this._renderTargetWidth, this._renderTargetHeight);
					}
					shaderString += "sampler2D " + textureName + "; ";
				}
				else{
					TextureManager.createSampler(texture.src, texture);
					shaderString += "sampler2D " + textureName+ "; ";
				}
			}
		}
		
		$.post(jsonObj.Shader.src, {}, function(string){
			shaderString += string;
			var material = MaterialManager.createMaterial(fxName);
			material.effect.loadFromFXString(shaderString);
			if (isset(jsonObj.Variable)) {
				//set the default value
				for(var variableName in jsonObj.Variable){
					var variable = jsonObj.Variable[variableName];
					material.effect.createUniformParameters(material);
					eval("material.getParam('" + variableName + "').value = variable.defaultValue");
				}
			}
			if(isset(jsonObj.Texture)){
				//set the sampler
				for(var textureName in jsonObj.Texture){
					//add texture to sampler later while the texture is a shadow map
					var texture = jsonObj.Texture[textureName];
					if(texture.semantic != "SHADOWMAP"){
						var sampler = TextureManager.getSampler(texture.src);
						eval("material.getParam('" + textureName + "').value = sampler" );
					}
				}
			}
			MaterialManager._options[fxName] = jsonObj;
			callback(material);
		})
	},
	
	/**
	 * update the param of the shader and option at the same time
	 * @param {String} name
	 * @param {String} param
	 * @param {Texture|String|Array} value
	 */
	updateParam : function(name, param, value){
		if(!isset(this._materials[name])){
			//error("material '" + name + "'not exits");
			return;
		}
		var material =	this._materials[name];
		
		if(!material.getParam(param)){
			//error("param '" + param +"' of material '" + name +"' not exits");
			return;
		}
		material.getParam(param).value = value;
		if(isset(this._options[name]) && this._options[name]){
			if(isset(this._options[name].Variable[param])){
				this._options[name].Variable[param].defaultValue = value;
			}		
		}
	},
	
	updateTexture : function(name, textureName, sampler, url){
		this._materials[name].getParam(textureName).value = sampler;
		if(isset(url)){
			this._options[name].Texture[textureName].url = url;
		}
	},
	
	/**
	 * 
	 * @param {String} newName
	 * @param {String} oldName
	 */
	updateMaterialName : function(newName, oldName){
		if(newName != oldName){
			if(!isset(this._materials[oldName])){
				error("material '" + oldName + "' not exits");
			}
			if(isset(this._options[oldName])){
				this._options[newName] = this._options[oldName];
				delete this._options[oldName];
			}
			this._materials[newName] = this._materials[oldName];
			this._materials[newName].name = newName;
			delete this._materials[oldName];
		}
	},
	
	/**
	 * copyt the param of source material to target material
	 * it will create a new material if the target material not exits
	 * @param {String} targetMaterial
	 * @param {String} sourceMaterial
	 * @return {Material}
	 */
	copyMaterial : function(targetMaterial, sourceMaterial){
		if(!isset(this.getMaterial(targetMaterial))){
			this.createMaterial(targetMaterial);
		}
		this.getMaterial(targetMaterial).copyParams(this.getMaterial(sourceMaterial));
		if(isset(this.getOption(sourceMaterial))){
			this._options[targetMaterial] = this._copyObj(this.getOption(sourceMaterial));
		}
		return this.getMaterial(targetMaterial);
	},
	
	_copyObj : function(obj){
		var json = toJSON(obj);
		return eval("("+json+")");
	},
	
	reset : function(){
		this._materials = [];
		this._options = [];
	}
}

MaterialManager.Helper = {
	createLambertMaterial : function(name){
	},
	
	createPhongMaterial : function(name, cameraName, color){
		var material = o3djs.material.createBasicMaterial(MaterialManager._pack, SceneManager.getCamera(cameraName).getViewInfo(), color);
		MaterialManager._materials[name] = material;
		
		return material;
		
	}
}
